package com.zzyl.nursing.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zzyl.common.constant.CacheConstants;
import com.zzyl.nursing.domain.AlertData;
import com.zzyl.nursing.domain.AlertRule;
import com.zzyl.nursing.domain.DeviceData;
import com.zzyl.nursing.mapper.AlertRuleMapper;
import com.zzyl.nursing.mapper.DeviceMapper;
import com.zzyl.nursing.service.IAlertDataService;
import com.zzyl.nursing.service.IAlertRuleService;
import com.zzyl.system.mapper.SysRoleMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 报警规则Service业务层处理
 * 
 * @author zzyl
 * @date 2025-10-28
 */
@Service
@Slf4j
public class AlertRuleServiceImpl extends ServiceImpl<AlertRuleMapper, AlertRule> implements IAlertRuleService
{
    @Autowired
    private AlertRuleMapper alertRuleMapper;

    @Autowired
    private IAlertDataService alertDataService;

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    private DeviceMapper deviceMapper;

    @Autowired
    private SysRoleMapper sysRoleMapper;

    @Value("${alertRule.roleAdmin}")
    String roleAdmin;

    @Value("${alertRule.roleRepair}")
    String roleRepair;

    /**
     * 查询报警规则
     * 
     * @param id 报警规则主键
     * @return 报警规则
     */
    @Override
    public AlertRule selectAlertRuleById(Long id)
    {
        return getById(id);
    }

    /**
     * 查询报警规则列表
     * 
     * @param alertRule 报警规则
     * @return 报警规则
     */
    @Override
    public List<AlertRule> selectAlertRuleList(AlertRule alertRule)
    {
        return alertRuleMapper.selectAlertRuleList(alertRule);
    }

    /**
     * 新增报警规则
     * 
     * @param alertRule 报警规则
     * @return 结果
     */
    @Override
    public int insertAlertRule(AlertRule alertRule)
    {
        return save(alertRule) ? 1 : 0;
    }

    /**
     * 修改报警规则
     * 
     * @param alertRule 报警规则
     * @return 结果
     */
    @Override
    public int updateAlertRule(AlertRule alertRule)
    {
        return updateById(alertRule) ? 1 : 0;
    }

    /**
     * 批量删除报警规则
     * 
     * @param ids 需要删除的报警规则主键
     * @return 结果
     */
    @Override
    public int deleteAlertRuleByIds(Long[] ids)
    {
        return removeByIds(Arrays.asList(ids)) ? 1 : 0;
    }

    /**
     * 删除报警规则信息
     * 
     * @param id 报警规则主键
     * @return 结果
     */
    @Override
    public int deleteAlertRuleById(Long id)
    {
        return removeById(id) ? 1 : 0;
    }

    /**
     * 定时进行设备数据对应的报警规则校验，将报警数据存储到对应的表中
     */
    @Override
    public void alertRuleJob() {
        // 1. 查询所有规则
        Long count = this.lambdaQuery().eq(AlertRule::getStatus, 1).count();
        // 2. 判断规则是否为空
        if (count <= 0){
            log.error("【定时报警规则校验】--无任何报警规则，无需处理");
            return;
        }
        // 3. 查询所有设备上报的数据
        List<Object> values = redisTemplate.opsForHash().values(CacheConstants.IOT_DEVICE_LAST_DATA_LATEST);
        // 4. 校验数据是否为空
        if (CollUtil.isEmpty(values)){
            log.error("【定时报警规则校验】--设备上报的数据为空，无需处理");
            return;
        }
        // 5. 数据格式标准化处理
        List<DeviceData> deviceDataList = new ArrayList<>();
        values.forEach(x-> {
            List<DeviceData> dataList = JSONUtil.toList((String) x, DeviceData.class);
            deviceDataList.addAll(dataList);
        });
        // 6. 遍历所有设备上报的数据
        for (DeviceData deviceData : deviceDataList) {
            // 6.1 校验设备上报数据的时间是否过早
            //设置最大间隔时间为1小时，单位为秒
            long maxInterval = 60 * 60;
            //上报时间
            LocalDateTime alarmTime = deviceData.getAlarmTime();
            //当前的时间
            LocalDateTime now = LocalDateTime.now();
            long interval= Math.abs(LocalDateTimeUtil.between(alarmTime, now, ChronoUnit.SECONDS));
            if (interval > maxInterval){
                log.error("【定时报警规则校验】--设备上报数据的时间是过早，无需处理，设备为{}，设备ID为{}，功能名为{}，上报时间为{}",deviceData.getNickname(),deviceData.getIotId(),deviceData.getFunctionId(),alarmTime);
                continue;
            }
            // 7. 获取对应的报警规则
            // 7.1 获取对当前上报数据对应的产品类型下同样的功能绑定生效的报警规则
            List<AlertRule> commonRules = this.lambdaQuery()
                    .eq(AlertRule::getProductKey, deviceData.getProductKey())
                    .eq(AlertRule::getFunctionId, deviceData.getFunctionId())
                    .eq(AlertRule::getIotId, -1)
                    .eq(AlertRule::getStatus, 1)
                    .list();
            // 7.2 获取当前上报数据对应设备对应的报警规则
            List<AlertRule> deviceRules = this.lambdaQuery()
                    .eq(AlertRule::getProductKey, deviceData.getProductKey())
                    .eq(AlertRule::getFunctionId, deviceData.getFunctionId())
                    .eq(AlertRule::getIotId, deviceData.getIotId())
                    .eq(AlertRule::getStatus, 1)
                    .list();
            // 7.3 合并规则集合，得到所有相关的规则
            Set<AlertRule> ruleSet = CollUtil.unionDistinct(commonRules, deviceRules);
            // 8. 校验对应的报警规则是否存在
            if (CollUtil.isEmpty(ruleSet)){
                log.error("【定时报警规则校验】--设备上报数据对应报警规则为空，无需处理，设备为{}，设备ID为{}，功能名为{}",deviceData.getNickname(),deviceData.getIotId(),deviceData.getFunctionId());
                continue;
            }
            // 9. 遍历每一条规则
            for (AlertRule alertRule : ruleSet) {
                // 10. 校验设备上报数据的时间是否在规则生效的时间内
                // 10.1 获取报警规则的生效时间段，格式为：00:00:00~23:59:59
                String alertEffectivePeriod = alertRule.getAlertEffectivePeriod();
                // 10.2 将生效时间段转化为LocalTime格式
                String[] time = alertEffectivePeriod.split("~");
                LocalTime startTime = LocalTime.parse(time[0]);
                LocalTime endTime = LocalTime.parse(time[1]);
                if (alarmTime.toLocalTime().isBefore(startTime) || alarmTime.toLocalTime().isAfter(endTime)){
                    log.error("【定时报警规则校验】--设备上报数据不在报警规则的生效时间段，无需处理，设备为{}，设备ID为{}，功能名为{}，对应的报警规则为{}，上报时间为{}，报警规则生效的时间段为{}",deviceData.getNickname(),deviceData.getIotId(),deviceData.getFunctionId(),alertRule.getAlertRuleName(),alarmTime,alertEffectivePeriod);
                    continue;
                }
                // 11. 校验数据是否达到报警规则所规定的阈值
                // 11.1 从报警规则中获取运算符
                String operator = alertRule.getOperator();
                // 11.2 从报警规则中获取阈值
                Double value = alertRule.getValue();
                // 11.3 获取上报数据的值
                Double dataValue = Double.valueOf(deviceData.getDataValue());
                if (("<".equals(operator) && value < dataValue) || (">=".equals(operator) && value >= dataValue)){
                    log.error("【定时报警规则校验】--设备上报数据没有超过阈值，无需处理，设备为{}，设备ID为{}，功能名为{}，对应的报警规则为{}，上报的值{}，没有{}阈值{}",deviceData.getNickname(),deviceData.getIotId(),deviceData.getFunctionId(),alertRule.getAlertRuleName(),dataValue,operator,value);
                    continue;
                }
                // 12. 校验报警规则是否处于沉默周期（从redis中查询）
                //redis中的沉默周期的key
                String silentKey = CharSequenceUtil.format(CacheConstants.IOT_ALERT_SILENT_PREFIX, deviceData.getIotId(), deviceData.getFunctionId(), alertRule.getId());
                String silentValue = redisTemplate.opsForValue().get(silentKey);
                if (StrUtil.isNotBlank(silentValue)){
                    log.error("【定时报警规则校验】--报警规则处于沉默周期中，无需处理，设备为{}，设备ID为{}，功能名为{}，对应的报警规则为{}",deviceData.getNickname(),deviceData.getIotId(),deviceData.getFunctionId(),alertRule.getAlertRuleName());
                    continue;
                }
                // 13. 计算当前的报警次数（从redis中获取）
                String alertCount = CharSequenceUtil.format(CacheConstants.IOT_ALERT_COUNT_PREFIX, deviceData.getIotId(), deviceData.getFunctionId(), alertRule.getId());
                Long increment = redisTemplate.opsForValue().increment(alertCount);
                Integer duration = alertRule.getDuration();
                if (increment < duration){
                    log.error("【定时报警规则校验】--报警规则未达到持续周期，无需处理，设备为{}，设备ID为{}，功能名为{}，对应的报警规则为{}，当前报警次数为{}，持续周期为{}",deviceData.getNickname(),deviceData.getIotId(),deviceData.getFunctionId(),alertRule.getAlertRuleName(),increment,duration);
                    continue;
                }
                // 14. 添加redis沉默周期
                Integer alertSilentPeriod = alertRule.getAlertSilentPeriod();
                redisTemplate.opsForValue().set(silentKey,"silent",alertSilentPeriod, TimeUnit.MINUTES);
                //删除报警次数
                redisTemplate.delete(alertCount);
                // 15. 判断数据为老人数据还是设备数据
                // 15.1 获取对应的员工id
                List<Long> nursingIdList = new ArrayList<>();
                if (alertRule.getAlertDataType() == 0){
                    //老人数据
                    //判断是移动数据还是固定数据
                    if (deviceData.getLocationType() == 0){
                        //移动设备（随身设备）
                        nursingIdList = deviceMapper.selectNuringIdsByMoveIotId(deviceData.getIotId());
                    }else {
                        //固定设备
                        nursingIdList = deviceMapper.selectNuringIdsByStopIotId(deviceData.getIotId());
                    }
                }else {
                    //设备数据（只查询维修员）
                    nursingIdList = sysRoleMapper.selectUserIdsByRoleName(roleRepair);
                }
                //查询管理员ids
                List<Long> adminIds = sysRoleMapper.selectUserIdsByRoleName(roleAdmin);
                //合并id集合
                Set<Long> userIdsSet = CollUtil.unionDistinct(nursingIdList, adminIds);
                //去除集合中的空值
                List<Long> userIds = userIdsSet.stream().filter(Objects::nonNull).collect(Collectors.toList());
                if (CollUtil.isEmpty(userIds)){
                    log.error("【定时报警规则校验】--没有查询到任何的用户id，无需处理，设备为{}，设备ID为{}，功能名为{}，对应的报警规则为{}",deviceData.getNickname(),deviceData.getIotId(),deviceData.getFunctionId(),alertRule.getAlertRuleName());
                    continue;
                }
                // 17. 批量保存数据
                //构建基础数据对象
                AlertData bean = BeanUtil.toBean(deviceData, AlertData.class);
                bean.setId(null);
                bean.setAlertRuleId(alertRule.getId());

                //报警原因，格式：功能名称+运算符+阈值+持续周期+沉默周期
                String reason =  alertRule.getFunctionName() + operator + value + "持续周期：" + duration + "，沉默周期：" + alertRule.getAlertSilentPeriod();
                bean.setAlertReason(reason);
                bean.setStatus(0);
                bean.setType(alertRule.getAlertDataType());

                List<AlertData> alertDataList = new ArrayList<>();
                for (Long userId : userIds) {
                    AlertData alertData = BeanUtil.toBean(bean, AlertData.class);
                    alertData.setUserId(userId);
                    alertDataList.add(alertData);
                }
                alertDataService.saveBatch(alertDataList);
            }

        }


    }
}
